package dk.simonvogensen.itodo.client.view;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.*;
import dk.simonvogensen.itodo.client.ITodoResources;
import dk.simonvogensen.itodo.client.Properties;
import dk.simonvogensen.itodo.client.controller.Controller;
import dk.simonvogensen.itodo.client.model.ProjectId;
import dk.simonvogensen.itodo.shared.model.TagLine;
import dk.simonvogensen.itodo.shared.model.TagItem;
import dk.simonvogensen.itodo.client.model.TransportType;
import dk.simonvogensen.itodo.shared.model.Config;
import dk.simonvogensen.itodo.shared.model.TodoItem;
import dk.simonvogensen.uirecorder.client.*;
import org.axed.user.client.AxedArea;
import org.axed.user.client.DOM;

import java.util.*;

public class View implements ITodoView {

    private Controller controller;

    private AxedArea axedArea;
    private Label headerLabel;
    private VerticalPanel axedAreaPanel;
    private VerticalPanel todoItemListPanel;
    private VerticalPanel logItemListPanel;

    // must be created before controller and model because of alert system
    private Label messageLabel = new Label("");

    private Button saveTodoButton;
    private Button nextTodoButton;
    private Button previousTodoButton;
    private Button deleteButton;

    private List<DialogBox> openDialogList = new ArrayList<DialogBox>();

//    ListBox deleteButton;

    private FocusPanel mainPanel;
    private Label infoLabel = new RecordableLabel("");
    private HorizontalPanel infoPanel;
    private FlowPanel projectsPanel;
    private HTML helpLink = new HTML("<a href=\"http://code.google.com/p/itodo/w/list\">help</a> <a href=\"http://code.google.com/p/itodo/issues/list\">issues</a>");
    Label logItemCountLabel;
    private VerticalPanel tagsListPanel;

    private String axedAreaItemId = "";

    public View() {
        Utils.setGenerateConfig(Utils.GenerateConfig.PREFIX_AND_GENERATE_IDS);
    }

    public void setController(Controller controller) {
        this.controller = controller;
    }

    public void init() {
        DockPanel dockPanel = new RecordableDockPanel();
        dockPanel.setWidth("100%");
        dockPanel.setHeight("100%");

        initDailyPanel(dockPanel);
        initLogPanel(dockPanel);

        RecordableRootPanel rPanel = new RecordableRootPanel();
        rPanel.getElement().setId("mainpanel");
        rPanel.add(dockPanel);
        mainPanel = new FocusPanel(rPanel);
        mainPanel.addKeyboardListener(getMainKeyboardListener());

        controller.init();
    }

    private void initLogPanel(DockPanel dockPanel) {
        VerticalPanel logPanel = new RecordableVerticalPanel();
        dockPanel.add(logPanel, DockPanel.EAST);
        dockPanel.setCellHorizontalAlignment(logPanel, HorizontalPanel.ALIGN_LEFT);
        logPanel.setSpacing(5);
        DOM.setStyleAttribute(logPanel.getElement(), "backgroundColor", "#DDDDFF");

        HorizontalPanel infoPanel = new RecordableHorizontalPanel();
        logPanel.add(infoPanel);
        Label label = new RecordableLabel("Backlog - ");
        infoPanel.add(label);

        logItemCountLabel = new RecordableLabel("");
        infoPanel.add(logItemCountLabel);

        final Button toogleAllNoneButton = new Button("None");
        toogleAllNoneButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                boolean value = toogleAllNoneButton.getText().equals("All");
                for (Widget widget : tagsListPanel) {
                    CheckBox cb = (CheckBox) widget;
                    cb.setValue(value);
                }
                toogleAllNoneButton.setText(value?"None":"All");
                updateLogItemList();
            }
        });
        infoPanel.add(toogleAllNoneButton);

        tagsListPanel = new VerticalPanel();
        tagsListPanel.setWidth("300px");
        tagsListPanel.getElement().setAttribute("style", "whiteSpace:nowrap;");
        logPanel.add(tagsListPanel);
        logPanel.setCellWidth(tagsListPanel, "300px");

        logItemListPanel = new RecordableVerticalPanel();
        logItemListPanel.setWidth("300px");
        logItemListPanel.setSpacing(5);
        logPanel.add(logItemListPanel);
    }

    public void initDailyPanel(DockPanel dockPanel) {
        VerticalPanel dailyPanel = new RecordableVerticalPanel();
        dailyPanel.setWidth("1000px");
        dockPanel.add(dailyPanel, DockPanel.CENTER);
        dockPanel.setCellHorizontalAlignment(dailyPanel, HorizontalPanel.ALIGN_RIGHT);
        dailyPanel.setSpacing(5);
        DOM.setStyleAttribute(dailyPanel.getElement(), "backgroundColor", "#DDDDFF");

        HorizontalPanel buttonPanel = new RecordableHorizontalPanel();
        buttonPanel.setWidth("100%");

        previousTodoButton = new RecordableButton("Previous", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.selectPreviousTodo();
            }
        });
        buttonPanel.add(previousTodoButton);

        nextTodoButton = new RecordableButton("Next", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.selectNextTodo();
            }
        });
        buttonPanel.add(nextTodoButton);

        buttonPanel.add(new HTML("&nbsp;"));

        Button newTodoButton = new RecordableButton("New", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.createNewTodo();
            }
        });
        buttonPanel.add(newTodoButton);

        saveTodoButton = new RecordableButton("Save", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.updateTodoItem(controller.getSelectedTodoItem());
            }
        });
        saveTodoButton.setEnabled(false);
        buttonPanel.add(saveTodoButton);

        deleteButton = new RecordableButton("Delete", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.deleteTodoItem();
            }
        });
        deleteButton.setEnabled(true);
        buttonPanel.add(deleteButton);

        final Button worktimeButton = new RecordableButton("Worktime", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.openWorktimeDialog();
            }
        });
        worktimeButton.setEnabled(true);
        buttonPanel.add(worktimeButton);

        buttonPanel.add(new HTML("&nbsp;"));
/*
        tagsListBox = new RecordableListBox();
        tagsListBox.addChangeHandler(new ChangeHandler() {
            public void onChange(ChangeEvent sender) {
                String tag = tagsListBox.getItemText(tagsListBox.getSelectedIndex());
                if (!"Tags..".equals(tag)) {
                    TagsDialogBox tagsDialogBox = new TagsDialogBox(controller, View.this, tag, TagItem.getTagItemsForTag(controller.getTodoItemList(), tag));
                    tagsDialogBox.setPopupPosition(tagsListBox.getAbsoluteLeft(), tagsListBox.getAbsoluteTop() + tagsListBox.getOffsetHeight() + 10);
                    openDialogList.add(tagsDialogBox);
                    tagsDialogBox.show();
                }
            }
        });
        buttonPanel.add(tagsListBox);
*/
        Button configButton = new RecordableButton("Config", new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.openConfigDialog();
            }
        });
        buttonPanel.add(configButton);

        Button recorderButton = new Button("Recorder", new ClickHandler() {
            public void onClick(ClickEvent event) {
                openRecorderDialog();
            }
        });
//        buttonPanel.add(recorderButton);

        buttonPanel.add(new HTML("&nbsp;"));

//        buttonPanel.add(new HTML("<a href=\"" + GWT.getHostPageBaseURL() + "export.php?pwd=" + backend.REQUEST_PWD + "\">export</a>"));

        DOM.setStyleAttribute(messageLabel.getElement(), "width", "100%");
        DOM.setStyleAttribute(messageLabel.getElement(), "textAlign", "center");
        buttonPanel.add(messageLabel);
        buttonPanel.setCellWidth(messageLabel, "100%");

        Label appNameLabel = new RecordableLabel("iTodo ");
        DOM.setStyleAttribute(appNameLabel.getElement(), "fontSize", "20px");
        buttonPanel.add(appNameLabel);
        Label buildLabel = new RecordableLabel("(beta build " + Properties.BUILDNUMBER + ")");
        DOM.setStyleAttribute(buildLabel.getElement(), "fontSize", "10px");
        DOM.setStyleAttribute(buildLabel.getElement(), "verticalAlign", "bottom");
        DOM.setStyleAttribute(buildLabel.getElement(), "whiteSpace", "nowrap");
        buttonPanel.add(buildLabel);

        dailyPanel.add(buttonPanel);

        infoPanel = new RecordableHorizontalPanel();
        infoPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
        dailyPanel.add(infoPanel);

        projectsPanel = new FlowPanel();
//        projectsPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
        dailyPanel.add(projectsPanel);

        axedAreaPanel = new RecordableVerticalPanel();
        headerLabel = new RecordableLabel();
        DOM.setStyleAttribute(headerLabel.getElement(), "fontSize", "12px");
        DOM.setStyleAttribute(headerLabel.getElement(), "backgroundColor", "white");
        DOM.setStyleAttribute(headerLabel.getElement(), "padding", "3px");
        DOM.setStyleAttribute(headerLabel.getElement(), "border", "1px solid black");
        DOM.setStyleAttribute(headerLabel.getElement(), "borderBottom", "none");
        axedAreaPanel.add(headerLabel);

        axedArea = new AxedArea(null, 1000, null);
        axedArea.setHeight(600);
        axedArea.setStyleName(ITodoResources.INSTANCE.css().axedArea()); 
//        axedArea.setStyleName("axedArea");
        axedArea.addKeyboardListener(getAxedAreaKeyboardListener());
        axedAreaPanel.add(axedArea);

        todoItemListPanel = new RecordableVerticalPanel();
        todoItemListPanel.setWidth("1002px");
        todoItemListPanel.setSpacing(5);
        dailyPanel.add(todoItemListPanel);
    }

    public void openWorktimeDialog() {
        WorktimeDialogBox worktimeDialogBox = new WorktimeDialogBox(controller, this);
        worktimeDialogBox.setPopupPosition(deleteButton.getAbsoluteLeft(), deleteButton.getAbsoluteTop() + deleteButton.getOffsetHeight() + 10);
        openDialogList.add(worktimeDialogBox);
        worktimeDialogBox.show();
    }

    public void openXmlWorktimeDialog() {
        XmlWorktimeDialogBox xmlWorktimeDialogBox = new XmlWorktimeDialogBox(controller);
        xmlWorktimeDialogBox.setPopupPosition(deleteButton.getAbsoluteLeft(), deleteButton.getAbsoluteTop() + deleteButton.getOffsetHeight() + 10);
        openDialogList.add(xmlWorktimeDialogBox);
        xmlWorktimeDialogBox.show();
    }

    public void openConfigDialog() {
        ConfigDialogBox configDialogBox = new ConfigDialogBox(controller);
        configDialogBox.setPopupPosition(deleteButton.getAbsoluteLeft(), deleteButton.getAbsoluteTop() + deleteButton.getOffsetHeight() + 10);
        openDialogList.add(configDialogBox);
        configDialogBox.show();
    }

    public void openRecorderDialog() {
        RecorderDialogBox recorderDialogBox = new RecorderDialogBox(controller);
        recorderDialogBox.setPopupPosition(deleteButton.getAbsoluteLeft(), deleteButton.getAbsoluteTop() + deleteButton.getOffsetHeight() + 10);
        recorderDialogBox.show();
    }

    public void updateProjectIdsAndTransportTypes() {
        Config config = controller.getConfig();
        infoPanel.clear();
        projectsPanel.clear();

        HorizontalPanel buttonPanel = new HorizontalPanel();
        for (final TransportType tt : config.getTransportTypeList()) {
            Button transportButton = new RecordableButton(tt.getName(), new ClickHandler() {
                public void onClick(ClickEvent event) {
                    controller.changeTransportType(tt);
                }
            });
            transportButton.setEnabled(true);
            buttonPanel.add(transportButton);
        }

        buttonPanel.add(new HTML("&nbsp;&nbsp;"));

        for (final ProjectId pi : config.getProjectIdList()) {
            Button timeButton = new RecordableButton(pi.getName(), new ClickHandler() {
                public void onClick(ClickEvent event) {
                    controller.addTime(pi);
                }
            });
            timeButton.setEnabled(true);
            projectsPanel.add(timeButton);
        }
        infoPanel.add(buttonPanel);

        infoPanel.add(new HTML("&nbsp;&nbsp;"));

        infoPanel.add(infoLabel);

        infoPanel.add(helpLink);
        infoPanel.setWidth("100%");
        infoPanel.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
    }
/*
    public void setTagsFocus(boolean focus) {
        tagsListBox.setFocus(focus);
    }
*/
    public void updateView() {
        updateTodoItemList();
        updateLogItemList();
        updateTagListBox();
        updateOvertimeLabel();
    }

    private void updateTodoItemList() {
        todoItemListPanel.clear();
        for (Object o : controller.getVisibleTodoItemList()) {
            final TodoItem item = (TodoItem) o;
            if (controller.getSelectedTodoItem() != null && item.equals(controller.getSelectedTodoItem())) {
                addItemArea();
            } else {
                addDailyItemHeader(item);
            }
        }
    }

    private void updateLogItemList() {
        int maxNumberOfTagLines = 30;
        List<String> activeTagList = new ArrayList<String>();
        for (Widget widget : tagsListPanel) {
            CheckBox checkBox = (CheckBox) widget;
            if(checkBox.getValue()) {
                activeTagList.add(checkBox.getText());
            }
        }
        logItemListPanel.clear();
        List<TagLine> tagLineList = TagItem.getTagLines(activeTagList, maxNumberOfTagLines, controller.getTodoItemList());
        logItemCountLabel.setText(TagItem.getTagLineCount(activeTagList, controller.getTodoItemList())+" items ");
        for (TagLine tagLine : tagLineList) {
            addLogItemHeader(tagLine);
        }
    }

    private void updateOvertimeLabel() {
        if (controller.getSelectedTodoItem() != null) {
            int minutes = 0;
            if (controller.getSelectedTodoItem().getPrevItem() != null)
                minutes = controller.getSelectedTodoItem().getPrevItem().getWorktimeInMonthUntilDate() - controller.getSelectedTodoItem().getPrevItem().getMinimumWorktimeInMonthUntilDate();
            infoLabel.setText("Overtime: " + minutes / 60 + "t" + minutes % 60);
        }
    }

    // try preserve checkstate on checkboxes that already and continues to exist
    private void updateTagListBox() {
        Set<TagItem> tagSet = TagItem.getTagSet(controller.getConfig(), controller.getTodoItemList());
        outer: for (Iterator<TagItem> it = tagSet.iterator(); it.hasNext();) {
            TagItem tagItem = it.next();
            // if found do nothing
            String checkBoxText = tagItem.getName();
            for (Widget widget : tagsListPanel) {
                CheckBox checkBox = (CheckBox) widget;
                if(checkBox.getText().equals(checkBoxText)) continue outer;
            }
            // if not found add it
            CheckBox checkBox = new CheckBox(checkBoxText);
            checkBox.setValue(true);
            checkBox.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    updateLogItemList();
                }
            });
            tagsListPanel.add(checkBox);
        }
        // remove what not found in tagset
        outer: for (Widget widget : tagsListPanel) {
            CheckBox checkBox = (CheckBox) widget;
            for (Iterator<TagItem> it = tagSet.iterator(); it.hasNext();) {
                if(checkBox.getText().equals(it.next().getName())) continue outer;
            }
            tagsListPanel.remove(widget);
        }
    }

    private void addItemArea() {
        todoItemListPanel.add(axedAreaPanel);
        todoItemListPanel.setCellHorizontalAlignment(axedArea, HorizontalPanel.ALIGN_LEFT);
        todoItemListPanel.setCellVerticalAlignment(axedArea, HorizontalPanel.ALIGN_TOP);
        todoItemListPanel.setCellHeight(axedArea, "100%");
        todoItemListPanel.setCellWidth(axedArea, "100%");
        headerLabel.setText(controller.getSelectedTodoItem().getHeaderText());

        // only update if changed item
        String selectedItemId = controller.getSelectedTodoItem().getId();
        if (!axedAreaItemId.equals(selectedItemId)) {
            refreshAxedArea();
            axedAreaItemId = selectedItemId;
        }
    }

    private void addDailyItemHeader(final TodoItem item) {
        Label headerTextLabel = new RecordableLabel();
        headerTextLabel.setText(item.getHeaderText());
        DOM.setStyleAttribute(headerTextLabel.getElement(), "fontSize", "12px");
        DOM.setStyleAttribute(headerTextLabel.getElement(), "backgroundColor", "white");
        DOM.setStyleAttribute(headerTextLabel.getElement(), "padding", "3px");
        DOM.setStyleAttribute(headerTextLabel.getElement(), "border", "1px solid black");

        headerTextLabel.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.selectTodo(item);
            }
        });
        todoItemListPanel.add(headerTextLabel);
    }

    private void addLogItemHeader(final TagLine tagLine) {
        HorizontalPanel hpanel = new HorizontalPanel();
        hpanel.setWidth("100%");
        DOM.setStyleAttribute(hpanel.getElement(), "fontSize", "12px");
        DOM.setStyleAttribute(hpanel.getElement(), "backgroundColor", "white");
        DOM.setStyleAttribute(hpanel.getElement(), "padding", "3px");
        DOM.setStyleAttribute(hpanel.getElement(), "border", "1px solid black");

        Label textLabel = new RecordableLabel();
        textLabel.setText(tagLine.getTag().getName() + tagLine.getText());
        textLabel.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.selectTodo(tagLine.getTag().getTodoItem());
            }
        });
        hpanel.add(textLabel);
        hpanel.setCellWidth(textLabel, "100%");

        Label doneLabel = new RecordableLabel("done");
        DOM.setStyleAttribute(doneLabel.getElement(), "color", "blue");
        doneLabel.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                controller.setLogItemDone(tagLine);
            }
        });
        hpanel.add(doneLabel);
        hpanel.setCellHorizontalAlignment(doneLabel, HorizontalPanel.ALIGN_RIGHT);


        logItemListPanel.add(hpanel);
    }

    public void refreshAxedArea() {
        axedArea.moveCursor(0, 0);
        axedArea.clear(axedArea.createAtomicID());
        String text = controller.getSelectedTodoItem().getText();
        if (text == null) text = "";
        axedArea.insertText(axedArea.createAtomicID(), 0, 0, text, null);
        axedArea.clearUndoRedoStack();
        DeferredCommand.addCommand(new Command() {
            public void execute() {
                axedArea.setFocus(true);
            }
        });
    }

    public boolean hasOpenDialogs() {
        return !openDialogList.isEmpty();
    }

    public void closeDialogs() {
        for (Iterator<DialogBox> it = openDialogList.iterator(); it.hasNext();) {
            DialogBox dialogBox = it.next();
            dialogBox.hide();
            it.remove();
        }
    }

    public void setAxedAreaFocus(boolean focus) {
        axedArea.setFocus(focus);
    }

    public void showAlert(String text) {
        System.out.println("Alert: " + text);
        messageLabel.setText(text);
        Timer timer = new Timer() {
            public void run() {
                messageLabel.setText("");
            }
        };
        timer.schedule(5000);
    }

    public String getSelectedTodoItemText() {
        return axedArea.getText1();
    }

    public int getLineOfCursor() {
        return axedArea.getCursorLine();
    }

    public int getLineCount() {
        return axedArea.getLineCount();
    }

    public void updateButtonStates(boolean enableNextButton, boolean enablePreviousButton,
                                   boolean enableSaveButton, boolean enableDeleteButton) {
        nextTodoButton.setEnabled(enableNextButton);
        previousTodoButton.setEnabled(enablePreviousButton);
        saveTodoButton.setEnabled(enableSaveButton);
        deleteButton.setEnabled(enableDeleteButton);
    }

    private KeyboardListener getMainKeyboardListener() {
        return new KeyboardListenerAdapter() {
            public void onKeyDown(Widget sender, char keyCode, int modifiers) {
                boolean preventDefault = controller.onGlobalKeyDown(keyCode, modifiers);
                if (preventDefault) {
                    DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
//                    DOM.eventCancelBubble(DOM.eventGetCurrentEvent(), true);
                }
            }
        };
    }

    boolean preventDefault;

    private KeyboardListener getAxedAreaKeyboardListener() {
        return new KeyboardListenerAdapter() {
            public void onKeyDown(Widget sender, char keyCode, int modifiers) {
                preventDefault = controller.onAxedAreaKeyDown(keyCode, modifiers);
                if (preventDefault) {
                    DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
                    DOM.eventCancelBubble(DOM.eventGetCurrentEvent(), true);
                }
//                showAlert("keydown:"+keyCode+" modifier:"+modifiers+" prevent:"+preventDefault);
            }

            public void onKeyUp(Widget sender, char keyCode, int modifiers) {
                controller.onAxedAreaKeyUp(keyCode, modifiers);
                if (preventDefault) {
                    DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
                    DOM.eventCancelBubble(DOM.eventGetCurrentEvent(), true);
                }
            }
        };
    }

    //TODO this and setAxedAreaFocus() should be merged
    public void unfocus() {
        mainPanel.setFocus(true);
    }

    public Panel getMainPanel() {
        return mainPanel;
    }
}